#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <regex>
#include "cdb_to_bdf_convertor.h"

CdbToBdfConvertor::CdbToBdfConvertor() {
}

CdbToBdfConvertor::~CdbToBdfConvertor() {
}

int CdbToBdfConvertor::convert(const std::string &cdbPath, const std::string &bdfPath) {
    int parseResult = parseCdbFile(cdbPath);
    if (parseResult != 0) {
        return parseResult;
    }

    return generateBdfFile(bdfPath);
}

int CdbToBdfConvertor::parseCdbFile(const std::string &path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        std::cerr << "Error opening CDB file: " << path << std::endl;
        return -1;
    }

    std::string line;
    std::string currentSection = "OTHER";

    while (getline(file, line)) {
        trim(line);
        if (line.empty()) {
            continue;
        }
        if (line.size() >= 4 && line.substr(0, 4) == "C***") {
            continue;
        }

	if (line.size() >= 2 && line.substr(0, 2) == "N,") {
            currentSection = "OTHER";
	}
	else if (line == "-1") {
            currentSection = "OTHER";
	}
        else if (line.find("NBLOCK") != std::string::npos) {
            currentSection = "NODE";
	}
        else if (line.find("EBLOCK")  != std::string::npos) {
            currentSection = "ELEMENT";
        }
	else if (line.find("SECTYPE") != std::string::npos) {
            currentSection = "SECTYPE";
        }

	if (currentSection == "NODE") {
            parseNode(line);
        } else if (currentSection == "ELEMENT") {
            parseElement(line);
        } else if (currentSection ==  "SECTYPE") {
            parsePropert(line);
	}
    }

    return 0;
}

void CdbToBdfConvertor::parseNode(const std::string &line) {
    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    // 分割字段
    while (iss >> token) {
        if (token == "-1") break; // 处理异常终止符
        tokens.push_back(token);
    }

    if (tokens.size() < 6) {
        return;
    }
    
    Node tmpNode;
    tmpNode.id = std::stoi(tokens[0]);
    tmpNode.x = std::stod(tokens[3]);
    tmpNode.y = std::stod(tokens[4]);
    tmpNode.z = std::stod(tokens[5]);
    gridNodes.push_back(tmpNode);
}

void CdbToBdfConvertor::parseElement(const std::string &line) {
    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    // 分割字段
    while (iss >> token) {
        if (token == "-1") break; // 处理异常终止符
        tokens.push_back(token);
    }

    if (tokens.size() < 2) {
        return ;
    }

    Element elem;
    elem.type = std::stoi(tokens[0]);

    // CQUAD4
    if (elem.type == 705002) {
        int nodeCount = std::stoi(tokens[8]);
        elem.id = std::stoi(tokens[10]);
	elem.propertyID =  std::stoi(tokens[2]);
	for (int i = 11; i < 11 + nodeCount; i++) {
            int tmpNodeId = std::stoi(tokens[i]);
            elem.nodes.push_back(tmpNodeId);
        }
    }
    // CHEXA
    else if (elem.type == 704002) {
        elem.id = std::stoi(tokens[1]);
	for (std::size_t i = 3; i < tokens.size(); i++) {
            int tmpNodeId = std::stoi(tokens[i]);
            elem.nodes.push_back(tmpNodeId);
        }
    }
    else if (elem.type == 702002) {
        elem.id = std::stoi(tokens[1]);
	for (int i = 3; i < 3 + 4; i++) {
            int tmpNodeId = std::stoi(tokens[i]);
            elem.nodes.push_back(tmpNodeId);
        }        
    }
    else {
        std::cout << "Unsupported element: " << line << std::endl;
        return;
    }
    gridElements.push_back(elem);
}

void CdbToBdfConvertor::parsePropert(const std::string &line) {
    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    // 分割字段
    while (std::getline(iss, token, ',')) {
        tokens.push_back(token);
    }

    if (tokens.size() < 2) {
        return ;
    }
    
    if (tokens[0] == "SECTYPE") {
        Property tmpProperty;
        tmpProperty.id = std::stoi(tokens[1]);
        tmpProperty.type = tokens[2];
        properties.push_back(tmpProperty);
    } else if (tokens[0] == "SECDATA") {
        properties.back().thickness = std::stod(tokens[1]);
        properties.back().materialId = std::stoi(tokens[2]);
    }
}

int CdbToBdfConvertor::generateBdfFile(const std::string &path) {
    std::ofstream outFile(path, std::ios::out);
    if (!outFile) {
        std::cerr << "Error creating BDF file: " << path << std::endl;
        return -1;
    }

    // 写入文件头
    outFile << "ID,1,1" << std::endl;
    outFile << "SOL 101" << std::endl;
    outFile << "CEND" << std::endl;
    outFile << "BEGIN BULK" << std::endl;

    // 写入节点（GRID卡）
    for (const auto& node : gridNodes) {
        outFile << std::left << std::setw(8) << "GRID" 
                << std::setw(8) << node.id 
                << std::setw(8) << " "
                << std::setw(8) << node.x
                << std::setw(8) << node.y
                << std::setw(8) << node.z 
                << std::endl;
    }

    // 写入单元
    for (const auto& elem : gridElements) {
        std::string elemType;
        switch (elem.type) {
            case 705002: elemType = "CQUAD4"; break;
            case 704002: elemType = "CHEXA";  break;
            case 702002: elemType = "CTETRA"; break;
            default: continue; // 跳过未知类型
        }

        outFile << std::left << std::setw(8) << elemType 
                << std::setw(8) << elem.id 
                << std::setw(8) << elem.propertyID;

        // 写入节点（每行最多8个）
        int count = 0;
        for (int nid : elem.nodes) {
            if (count % 8 == 0 && count != 0) {
                outFile << "\n        "; // 续行符
            }
            outFile << std::setw(8) << nid;
            count++;
        }
        outFile << "\n";
    }

    // 写入属性
    for (const auto &property: properties) {
        std::string propertyType;
        if (property.type == "SHELL") {
            propertyType = "PSHELL";
        } else {
            continue;
        }
        outFile << std::left <<  std::setw(8) << propertyType
                <<  std::setw(8) << property.id
                << std::right << std::setw(8) << property.materialId
                << std::setw(8) << property.thickness
                << std::setw(8) << property.materialId
                << std::setw(8) << 1.0
                << std::setw(8) << property.materialId
                << std::setw(8) << 0.0
                << std::endl;
    }

    outFile << "ENDDATA\n";
    return 0;
}

void CdbToBdfConvertor::trim(std::string& s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

